<?php
namespace App\Handlers;

use App;
use App\Models\Device;
use App\Models\ChargeRecord;
use App\Models\DeviceRecord;
use App\Jobs\SendHeartBeat;
use App\Services\Product\DeviceService;
use App\Services\Infra\DeviceManager;
use App\Utils\CRC16;
use App\Utils\DeviceServer;
use Carbon\Carbon;
use Cache;
use Exception;
use Illuminate\Foundation\Bus\DispatchesJobs;

class SwooleHandler{
	use DispatchesJobs;

	public function _construct(){
	}

	/**
	 * tcp 连接开始
	 * @param  [type] $serv 
	 * @return [type]       
	 */
	public function onStart($serv){
	}

	/**
	 * tcp 连接
	 * @param  [type] $serv    
	 * @param  [type] $fd      
	 * @param  [type] $from_id 
	 * @return [type]          
	 */
	public function onConnect($serv, $fd, $from_id){
		try{
            //非本机TCP连接fd号记录
			// if($serv->connection_info($fd)['server_port'] == env('WEB_PORT')){
				// info([
				// 	'connect' => $fd,
				// 	]);
		       	// $job = (new SendHeartBeat($fd))->delay(3)->onQueue('heartBeat');
		       	// $this->dispatch($job);
			// }
		}catch(Exception $e){
			logger(['SwooleHandler@onConnect' => [
				'line' => $e->getLine(),
				'file' => $e->getFile(),
				'message' => $e->getMessage(),
				]]);
		}
	}

	/**
	 * tcp 连接接受到消息
	 * @param  [type] $serv    
	 * @param  [type] $fd      
	 * @param  [type] $from_id 
	 * @param  [type] $data    
	 * @return [type]          
	 */
	public function onReceive($serv, $fd, $from_id, $data){
		try{
			if(strlen($data) >= 20){

                // 获取缓存，如果存有key值isLog则将数据存进去，否则为空？？？
                Cache::get('isLog')? logger(['fd' => $fd, 'data' => bin2hex($data)]): '';

                // 将接收到的信息存到缓存中
				$this->handle($serv, $fd, $data);
			}
		}catch(Exception $e){
			logger(['SwooleHandler@onReceive' => [
				'line' => $e->getLine(),
				'file' => $e->getFile(),
				'message' => $e->getMessage(),
				]]);
		}
	}


	public function handle($serv, $fd, $data){
        $hexString = bin2hex($data);
        $len = hexdec(substr($hexString, 8, 2));
        $subData = substr($data, 0, $len + 5);
        $result = CRC16::check(substr($subData, 0, strlen($subData) - 2), substr($subData, strlen($subData) - 2));
		if($result){
            // 将数据放到缓存中
			$this->resolve($serv, $subData, $fd);
		}
		$unhandleData = substr($data, $len +5);
		if(substr_count(bin2hex($unhandleData), 'bb000780') > 0){
			$this->handle($serv, $fd, $unhandleData);
		}
	}

	/**
	 * tcp 关闭
	 * @param  [type] $serv    
	 * @param  [type] $fd      
	 * @param  [type] $from_id 
	 * @return [type]          
	 */
	public function onClose($serv, $fd, $from_id){
		$fds = [];
        foreach ($serv->connections as $connection) {
        	if($connection != $fd){
        		$fds[] = $connection;
        	}
        }
        Cache::put('fds', $fds, Carbon::now()->addMinutes(60));
		if($serv->connection_info($fd)['server_port'] == env('WEB_PORT')){
			// info([
			// 	'close' => $fd,
			// 	]);
		}
	}

	/**
	 * data resolve
	 * @param  [type] $serv 
	 * @param  [type] $data 
	 * @param  [type] $fd   
	 * @return [type]       
	 */
	public function resolve($serv, $data, $fd)
	{
    	$u = unpack("nflag/ncmd/CdataLength/NmessageNumber/N2decCode/CdataCmd/Coperation", $data);

        if($u['flag'] == 0xbb00 && $u['cmd'] == 0x0780)
        {
            $fds = [];
            foreach ($serv->connections as $connection) {
            	$fds[] = $connection;
            }
            $fdinfo = $serv->connection_info($fd);
            (new DeviceService())->handle($u['dataCmd'], $fd, $data, $fdinfo);
            
            Cache::put('fds', $fds, Carbon::now()->addMinutes(60));
        }

        switch ($u['dataCmd']) {
        	case 0x05:
            	$heartBeatData = pack('n2CN3C', 0xbb00, 0x0780, 0x0f, $u['messageNumber'], $u['decCode1'], $u['decCode2'], 0x06);
            	$crc = CRC16::crc16($heartBeatData);
            	$heartBeatCmd = $heartBeatData.pack('n', $crc);
				$serv->send($fd, $heartBeatCmd);
        		break;
        	case 0x09:
        		$chargeRecord = ChargeRecord::where('id', $u['messageNumber'])->first();
        		if(!is_null($chargeRecord)){
                    $this->rechargeReplyProcess($u['operation'], $chargeRecord);
        		}
        		break;
        	case 0x0b:
        		$deviceCode = (new DeviceServer)->getDeviceCode(substr($data, 9, 8));
        		if(!$this->updateDeviceStatus($deviceCode, $u['messageNumber'], 1)){
        			$this->updateDeviceStatus($deviceCode, $u['messageNumber'], 0);
        		}
                break;
            case 0x0e:
                $cmd = (new DeviceServer())->getTimestampCmd(substr($data, 5, 12));
                $serv->send($fd, $cmd);
                break;
            case 0x0d:
                $chargeRecord = ChargeRecord::where('id', $u['messageNumber'])->first();
                if(!is_null($chargeRecord)){
                    if(strlen($data) == 27){
                        $dataArray = unpack('Cstatus/Cmode/Coperation/NresidualAmount', substr($data, 18, 7));
                        if($dataArray['status'] == 0){
                            $this->rechargeReplyProcess($dataArray['operation'], $chargeRecord, $dataArray['mode'], $dataArray['residualAmount']);
                        }
                    }

                }
                break;
        	default:
        		# code...
        		break;
        }
	}

	public function updateDeviceStatus($deviceCode, $messageNumber, $conExt)
	{
		$haltMessage = Cache::get($deviceCode.'haltMessage'.$conExt);
		if($haltMessage['messageNumber'] == $messageNumber){
    		if ($haltMessage['conExt'] == 1) {
				(new DeviceManager)->updateDevice($deviceCode, ['status' => 2], $conExt);
    		}else if($haltMessage['conExt'] == 0){
				(new DeviceManager)->updateDevice($deviceCode, ['status' => 0], $conExt);
    		}else{
    		}
    		return true;
    	}else{
    		return false;
    	}
	}

    /**
     * 充值回复处理
     * @param  integer $operation       增减模式
     * @param  object  $chargeRecord    待处理充值记录对象
     * @param  integer  $mode           充值模式
     * @param  integer  $residualAmount 剩余量
     * @return                
     */
    public function rechargeReplyProcess($operation, $chargeRecord, $mode = 0, $residualAmount = 0)
    {
        if($operation == 0x2b && in_array($chargeRecord->status, [ChargeRecord::ORDER_STATUS_PAID, ChargeRecord::ORDER_STATUS_OFFLINE_RECHARGE])){
            $updateArray = ['status' => ChargeRecord::ORDER_STATUS_RECHARGE];
        }else if($operation == 0x2d && $chargeRecord->status == ChargeRecord::ORDER_STATUS_DISCHARGE){
            $updateArray = ['status' => ChargeRecord::ORDER_STATUS_DISCHARGED];
        }else{
            $updateArray = [];
        }
        $updateArray = $this->formatRechargeReplyUpdateArray($updateArray, $mode, $residualAmount);

        if(count($updateArray) && ($chargeRecord->recharge_mode == $mode || $mode == 0)){
            $chargeRecord->update($updateArray);
        }
        $chargeRecord->load(['device' => function($query){
            $query->select(['id', 'code', 'recharge_mode']);
        }]);
        $chargeRecord->device->update(['recharge_mode' => $mode]);
        $deviceCode = $chargeRecord->device->code;
        $state = Cache::get($deviceCode.'state');
        switch($mode)
        {
            case 1:
                $state['timeLimit'] = $residualAmount;
                break;
            case 2:
                $state['residualFlow'] = $residualAmount;
                $state['timeLimit'] = 0;
                break;
            default:
                break;
        }
        Cache::put($deviceCode.'state', $state, env('CACHE_DEVICE_DATA'));
        Cache::forever($deviceCode.'recharge', 1);
    }

    public function formatRechargeReplyUpdateArray(array $updateArray, $mode, $residualAmount)
    {
        switch($mode)
        {
            case 1:
                $updateArray = array_merge($updateArray, ['time_limit' => $residualAmount]);
                break;
            case 2:
                $updateArray = array_merge($updateArray, ['residual_flow' => $residualAmount]);
                break;
            default:
                break;
        }
        return $updateArray;
    }

}